import json
from apps.interface.settings import config
from apps.interface.util.global_variable import *
from flask import current_app


def get_type_dict():
    return {'http': KeyWordHttp(), 'mysql': KeyWordMySql(),
            'dubbo': KeyWordDubbo(), 'redis': KeyWordRedis()}


def handle_url(api_data, pro_base_url):
    """

    :param api_data:
    :param pro_base_url:
    :return:
    """
    env_url = pro_base_url['{}'.format(api_data.project_id)]
    if not env_url:
        raise Exception('"{}"的环境IP未找到，请检查服务配置'.format(api_data.name))
    pro_base_url = pro_base_url['{}'.format(api_data.project_id)][
                       int(api_data.status_url)] + api_data.url.split('?')[0]
    return pro_base_url


class SuperRun():
    def __init__(self, _data, api_data, step_data, global_headers, status):
        _data = self.get_data(_data, api_data, step_data, global_headers, status)
        
    def get_data(self, _data, api_data, step_data, global_headers, status):
        """
        处理公共数据
        headers、url、setup_hooks、teardown_hooks
        extract、validate、params、par_var_ext_val
        :param _data:
        :param api_data:
        :param step_data:
        :param global_headers:
        :param status:
        :return:
        """
        # 处理headers
        _data['request']['headers'] = self.get_headers(api_data, global_headers)
        # 处理url
        _data['request']['url'] = self.get_url(_data, api_data)
        # 处理hook
        if step_data.up_func:
            _data['setup_hooks'] = json.loads(step_data.up_func)
        if step_data.down_func:
            _data['teardown_hooks'] = json.loads(step_data.down_func)
        par_var_ext_val = self.handle_par_var_ext_val(step_data, api_data, status)
        # 处理extract值List转Dict
        _data['extract'] = self.handle_extract(par_var_ext_val.get('_extract'))
        _data['validate'] = self.handle_validate(par_var_ext_val.get('_validate'))
        _data['request']['params'] = self.get_params(api_data, par_var_ext_val.get('_param'))
        _data['par_var_ext_val'] = par_var_ext_val
        return _data
    
    def get_url(self, _data, api_data):
        """
        
        :param _data:
        :param api_data:
        :return:
        """
        # 非http请求url
        if api_data.status_url != '-1':
            # 非http请求走Bourne服务
            if api_data.api_type != 'http':
                return config.EXTENSION_URL
        else:
            return api_data.url
    
    def get_headers(self, api_data, global_headers):
        """
        
        :param api_data:
        :param global_headers:
        :return:
        """
        if api_data.api_type in ['dubbo', 'mysql', 'redis']:
            headers = {'Content-Type': 'application/json;charset=UTF-8'}
        else:
            headers = dict(global_headers)
            headers.update({h['key']: h['value'] for h in json.loads(api_data.header)
                            if h['key']} if json.loads(api_data.header) else {})
        return headers
    
    def get_params(self, api_data, _param):
        if api_data.api_type == 'mysql':
            params = {}
        elif api_data.method != 'GET':
            params = {}
            for param in _param:
                if param.get('key'):
                    # 为None时，置为''
                    if not param.get('value'):
                        param['value'] = ''
                    params[param['key']] = param.get('value').replace('%', '&')
        else:
            params = {}
        return params
    
    def handle_par_var_ext_val(self, step_data, api_data, status):
        if status:
            _param = self.variable_method(step_data.status_param,
                                          json.loads(step_data.param),
                                          json.loads(api_data.param))
            _json_variables = self.variable_method(step_data.status_variables,
                                                   step_data.json_variable,
                                                   api_data.json_variable)
            _variables = self.variable_method(step_data.status_variables,
                                              json.loads(step_data.variable),
                                              json.loads(api_data.variable))
            _extract = self.variable_method(step_data.status_extract,
                                            step_data.extract,
                                            api_data.extract)
            _validate = self.variable_method(step_data.status_validate,
                                             step_data.validate,
                                             api_data.validate)
        else:
            _param = json.loads(api_data.param)
            _json_variables = api_data.json_variable
            _variables = json.loads(api_data.variable)
            _extract = api_data.extract
            _validate = api_data.validate
        return {'_param': _param, '_json_variables': _json_variables, '_variables': _variables,
                '_extract': _extract, '_validate': _validate}
    
    def variable_method(self, step_data_status, step_data, variable):
        if json.loads(step_data_status)[0]:
            if json.loads(step_data_status)[1]:
                _param = step_data
            else:
                _param = variable
        else:
            _param = None
        return _param
    
    def handle_extract(self, _extract):
        # 处理extract值List转Dict
        extract_dict = {ext['key']: ext['value']
                        for ext in json.loads(_extract) if ext['key'] if ext['value']}
        return extract_dict
    
    def handle_validate(self, _validate):
        # 如果comparator与key都不存在忽略
        validate = [{val['comparator']: [val['key'], val['value']]}
                    for val in json.loads(_validate)
                    if val.get('key') if val.get('comparator')] if _validate else []
        return validate


class KeyWordHttp():
    
    def get_data(self, _data, api_data, pro_base_url):
        var = _data.get('par_var_ext_val')
        _data['request']['url'] = self.http_url(_data, api_data, pro_base_url, var.get('_param'))
        _data['request']['params'] = self.http_params(api_data, var.get('_param'))
        if api_data.method == 'GET':
            return _data
        if api_data.method != 'GET':
            if (api_data.variable_type == 'text' or api_data.variable_type == 'data') and \
                    var.get('_variables'):
                _data = self.http_variable(_data, api_data.variable_type,
                                           _data['par_var_ext_val']['_variables'])
        if var.get('_json_variables'):
            _data = self.http_json(_data, var.get('_json_variables'))
        return _data
    
    def http_url(self, _data, api_data, pro_base_url, _param):
        """

        :param _data:
        :param api_data:
        :param pro_base_url:
        :param _param:
        :return:
        """
        if api_data.method == 'GET':
            # 拼接前缀url
            prefix_url = '{}?'.format(handle_url(api_data, pro_base_url))
            param_list = []
            for param in _param:
                if param.get('key'):
                    # 为None时，置为'' get、post都需要处理param的value为空
                    if not param.get('value'):
                        param['value'] = ''
                    param_list.append(param['key'] + '=' + param.get('value').replace('%', '&'))
            url_param = '&'.join(param_list)
            # 拼接总url
            base_url = prefix_url + url_param
            return base_url
        else:
            # 取域名方法一致
            url = handle_url(api_data, pro_base_url)
        return url
    
    def http_params(self, api_data, _param):
        if api_data.method != 'GET':
            params = {param['key']: param['value'].replace('%', '&')
                      for param in _param if param.get('key')} if _param else {}
        else:
            params = {}
        return params
    
    def http_variable(self, _data, variable_type, _variables):
        for variable in _variables:
            if variable_type == 'text':
                files = 'files'
                variable_value = (None, variable['value'])
            else:
                # variable_type == 'data'
                files = 'data'
                variable_value = variable['value']
            if variable['param_type'] == 'string' and variable.get('key'):
                _data['request'][files].update({variable['key']: variable_value})
            elif variable['param_type'] == 'file' and variable.get('key'):
                _data['request']['files'].update({
                    variable['key']: (
                        variable['value'].split('/')[-1], open(variable['value'], 'rb'),
                        CONTENT_TYPE['.{}'.format(variable['value'].split('.')[-1])])
                })
        return _data
    
    def http_json(self, _data, _json_variables):
        body = json.loads(_json_variables)
        if isinstance(body, list):
            # 通过json转中文
            _data['request']['data'] = json.dumps(body)
        if isinstance(body, dict):
            _data['request']['json'] = body
        if isinstance(body, str):
            # 处理变量引用
            _data['request']['data'] = body
        return _data


class KeyWordMySql():
    
    def get_data(self, _data, api_data, pro_base_url):
        var = _data.get('par_var_ext_val')
        if var.get('_json_variables') and api_data.api_type == 'mysql' \
                and api_data.variable_type == 'json':
            _data = self.mysql_json(_data, api_data, pro_base_url, var['_json_variables'],
                                    var['_param'])
        return _data
    
    def mysql_json(self, _data, api_data, pro_base_url, _json_variables, _param):
        mysql_variables = json.loads(_json_variables)
        # 去除sql首位空格
        mysql_variables['sql'] = mysql_variables.get('sql').strip()
        mysql_host = handle_url(api_data, pro_base_url)
        mysql_database = mysql_variables['user'] = mysql_variables['password'] = ''
        for parameter in _param:
            if parameter['key'] == 'user':
                mysql_variables['user'] = parameter['value']
            
            if parameter['key'] == 'password':
                mysql_variables['password'] = parameter['value']
            
            if parameter['key'] == 'database':
                mysql_database = parameter['value']
        
        mysql_variables['jdbc'] = \
            'jdbc:mysql://{host}/{database}?characterEncoding=utf8&useSSL=false'.format(
                host=mysql_host, database=mysql_database)
        d_request = {
            "interfaceId": api_data.num,
            "interfaceName": api_data.name,
            "method": "mysql",
            "projectId": api_data.project_id,
            "request": {
                "body": json.dumps(mysql_variables),
                "headers": {},
                "pathParams": {},
                "queryParams": {}
            },
            "type": api_data.api_type,
            "version": "",
            "zkAddress": ""
        }
        _data['request']['json'] = d_request
        return _data


class KeyWordDubbo():
    
    def get_data(self, _data, api_data, pro_base_url):
        """
        
        :param _data:
        :param api_data:
        :param pro_base_url:
        :return:
        """
        var = _data.get('par_var_ext_val')
        if var.get('_json_variables') and api_data.variable_type == 'json':
            _data['request']['json'] = self.dubbo_json(api_data, pro_base_url,
                                                       var.get('_json_variables'))
        return _data
    
    def dubbo_json(self, api_data, pro_base_url, _json_variables):
        d_request = {
            "interfaceId": api_data.num,
            "interfaceName": api_data.dubbo_interface,
            "method": api_data.dubbo_method,
            "projectId": api_data.project_id,
            "request": {
                "body": _json_variables,
                "headers": {},
                "pathParams": {},
                "queryParams": {}
            },
            "type": api_data.api_type,
            "version": api_data.dubbo_version,
            "zkAddress": handle_url(api_data, pro_base_url)
        }
        
        return d_request


class KeyWordRedis():
    
    def get_data(self, _data, api_data, pro_base_url):
        """
        
        :param _data:
        :param api_data:
        :param pro_base_url:
        :return:
        """
        var = _data.get('par_var_ext_val')
        if var.get('_json_variables') and api_data.variable_type == "json":
            _data["request"]["json"] = self.redis_json(_data, api_data, var.get("_json_variables"),
                                                       pro_base_url, var.get("_param"))
        return _data
    
    def redis_json(self, _data, api_data, _json_variables, pro_base_url, _param):
        prefix_url = pro_base_url['{}'.format(api_data.project_id)][int(api_data.status_url)]
        if prefix_url.find("http") != -1:
            raise Exception("redis域名不对")
        url = prefix_url.split(':')
        redis_variables = {}
        for parameter in _param:
            if parameter["key"] == "password":
                redis_variables["password"] = parameter["value"]
            elif parameter["key"] == "database":
                redis_variables["database"] = parameter["value"]
            elif parameter["key"] == 'operation':
                redis_variables["operation"] = parameter["value"]
        redis_variables["host"] = url[0]
        redis_variables["port"] = url[1]
        
        d_request = self.split_request(api_data, redis_variables, _json_variables)
        
        return d_request
    
    def split_request(self, api_data, redis_variables, _json_variables):
        # 根据方法类型组装redis中的content
        redis_variables = self.redis_content(redis_variables, _json_variables)
        d_request = {
            "interfaceId": api_data.num,
            "interfaceName": api_data.name,
            "method": api_data.api_type,
            "projectId": api_data.project_id,
            "request": {
                "body": json.dumps(redis_variables),
            },
            "pathParams": {
            },
            "queryParams": {
            },
            "type": api_data.api_type,
            "version": "",
            "zkAddress": ""
        }
        return d_request
    
    def redis_content(self, redis_variables, _json_variables):
        var = json.loads(_json_variables)
        # 判断redis的方法
        if redis_variables["operation"] == "GET":
            redis_variables["content"] = {"key": var.get("key")}
            current_app.logger.info("redis的key：{}".format(redis_variables["operation"]))
        elif redis_variables["operation"] == "SET":
            redis_variables["content"] = {"key": var.get("key"), "value": var.get("value")}
            current_app.logger.info("redis的key：{}".format(var.get("key")))
            current_app.logger.info("redis的value：{}".format(var.get("value")))
        else:
            raise Exception("redis的方法未开发")
        if not redis_variables["content"]["key"]:
            raise Exception("redis输入key或value有误")
        return redis_variables
